package conf

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"time"
)

//Com 根据操作系统类型(Unix or Windows)判断注释类型
var Com byte

//ErrFailOpen 意味着打开配置文件失败
var ErrFailOpen = errors.New("[Error]failed to open file")

//ErrFailRead 意味着读取配置文件失败
var ErrFailRead = errors.New("[Error]failed to read file")

//ErrNotKeyValue 意味着配置文件中出现不是键值对的配置信息
var ErrNotKeyValue = errors.New("[Error]配置信息出错了!====不是键值对")

//ErrBlankInKey 意味着配置文件中的key值之间出现了空格
var ErrBlankInKey = errors.New("[Error]配置信息出错了!====key之间不能有空格")

//ErrBlankInValue 意味着配置文件中的value值之间出现了空格
var ErrBlankInValue = errors.New("[Error]配置信息出错了!====value之间不能有空格")

//Listener 该接口用来监听配置文件是否被修改
type Listener interface {
	listen(inifile string)
}

//ListenFunc 是一个接收一个接口类型的参数的函数变量类型
type ListenFunc func(string)

//ListenFunc 的一个方法，实现具体的监听过程，每2s监听一次，如果配置文件发生改变，
//该函数执行完毕，退出该函数，否则隔2s后接着监听，直到监听到配置文件发生改变。
func (a ListenFunc) listen(inifile string) {
	file1 := readFile(inifile)
	for {
		time.Sleep(time.Second * 2)
		file2 := readFile(inifile)
		if file1 == file2 {
			fmt.Println("============Listening...==============")
			continue
		} else {
			break
		}
	}
}

//提取文件内容，用来判断文件是否发生改变
func readFile(filename string) string {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	buffer := bufio.NewReader(file)
	var chunks []byte

	buf := make([]byte, 1024)

	for {
		_, err := buffer.Read(buf)
		if err != nil && err != io.EOF {
			panic(err)
		}

		if err == io.EOF {
			break
		}

		//fmt.Println(string(buf))
		chunks = append(chunks, buf...)

	}
	return string(chunks)
	//fmt.Println(string(chunks))
}

//Watch 接收一个string类型的配置文件名变量和一个Listener类型的监听接口变量，然后监听自函数执行以来
// 发生的一次配置文件变化，当监听到配置文件发生变化时，该函数继续执行解析改变后的配置文件，并返回key-value
// 式样的配置文件解析结果和自定义错误。这里的自定义错误包括ErrFailOpen、ErrFailRead、ErrNotKeyValue、
// ErrBlankInKey和ErrBlankInValue五个自定义错误。
func Watch(filename string, listener Listener) (configuration map[string]string, err error) {
	listener.listen(filename)

	file, err1 := os.Open(filename)
	if err1 != nil {
		err = ErrFailOpen
		return configuration, err
	}
	buffer := bufio.NewReader(file)
	//为configuration分配空间
	configuration = make(map[string]string)
	kv := make(map[string]string)
	isTrue := true

	//去掉注释，提取键值对kv(map[string]string)中，如果有不是键值对且不是注释的情况，报错
	for {
		var eachLine string
		eachLine, err2 := buffer.ReadString('\n')

		//如果文件中有空行且不是最后一行，直接跳过该行处理下一行
		if len(eachLine) == 2 {
			continue
		}

		//如果文件结束处是一个空行，直接跳出处理过程
		if len(eachLine) == 0 {
			break
		}

		//检查是否出错
		if err2 != nil && err2 != io.EOF {
			err = ErrFailRead
			return configuration, err
		}

		// fmt.Print(eachLine)
		// fmt.Println(len(eachLine))

		var confLine = []rune(eachLine)
		//var temp = make([]rune, len(confLine))
		var key = make([]rune, 0)
		var value = make([]rune, 0)
		isConf := false //判断是不是有效配置信息（通过判断有无'='来体现）
		isOk := false   //区别字符串的key值和value值部分

		for i := 0; i < len(confLine); i++ {
			if confLine[i] == rune(Com) || confLine[i] == '[' {
				break
			} else if confLine[i] == '\n' {
				continue
			}
			if confLine[i] == '=' {
				isConf = true
				isOk = true
				continue
			}

			if !isOk {
				key = append(key, confLine[i])
			} else {
				value = append(value, confLine[i])
			}
		}

		if isConf {
			kv[string(key)] = string(value)
		}

		if !isConf && len(key) != 0 {
			isTrue = false
			err = ErrNotKeyValue
			return configuration, err
		}

		//读取到文件最后，直接跳出处理过程
		if err2 == io.EOF {
			break
		}
	}

	//判断有没有不是键值对的出错
	if !isTrue {
		os.Exit(0)
	}

	for k, v := range kv {
		//fmt.Println(k)
		var key1 = make([]rune, 0)
		var value1 = make([]rune, 0)
		k1 := []rune(k)
		v1 := []rune(v)

		//判断是不是第一个非空格字符
		isYes := false
		//判断是不是最后非空格字符
		isLast := true
		for i := 0; i < len(k1); i++ {
			if !isYes && k1[i] == ' ' {
				continue
			} else if !isYes || k1[i] != ' ' {
				isYes = true
				key1 = append(key1, k1[i])
			} else if isYes && k1[i] == ' ' {
				for j := i; j < len(k1); j++ {
					//attention
					if k1[j] != ' ' && k1[j] != 13 {
						isLast = false
						break
					}
				}
				if !isLast {
					err = ErrBlankInKey
					return configuration, err
				}
			}
		}

		//判断是不是第一个非空格字符
		isYes = false
		//判断是不是最后非空格字符
		isLast = true
		for i := 0; i < len(v1); i++ {
			isLast := true
			if !isYes && v1[i] == ' ' {
				// fmt.Println(i)
				// fmt.Println("hello")
				continue
			} else if !isYes || v1[i] != ' ' {
				isYes = true
				value1 = append(value1, v1[i])
			} else if isYes && v1[i] == ' ' {
				for j := i; j < len(v1); j++ {
					//attention
					if v1[j] != 32 && v1[j] != 13 {
						isLast = false
						break
					}
				}
				if !isLast {
					err = ErrBlankInValue
					return configuration, err
				}
			}
		}

		if value1[len(value1)-1] == 13 {
			//去掉value1末尾残留的一个字符
			value2 := make([]rune, len(value1)-1)
			for i := 0; i < len(value1)-1; i++ {
				value2[i] = value1[i]
			}
			configuration[string(key1)] = string(value2)
		} else {
			configuration[string(key1)] = string(value1)
		}
	}

	return configuration, err
}

//init 函数判断该系统类型是Unix还是Windows
func init() {
	sysType := runtime.GOOS

	if sysType == "linux" {
		// LINUX系统
		Com = '#'
	}

	if sysType == "windows" {
		// Windows系统
		Com = ';'
	}
}
